// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT

package metrics

import (
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/sirupsen/logrus"
	"gopkg.in/yaml.v2"
)

const defaultConfigPath = "/etc/uos-exporter/time-exporter.yaml"

var (
	configMu         sync.RWMutex
	activeConfig     *TimeConfig
	activeConfigPath = defaultConfigPath
)

type TimeConfig struct {
	Collectors   CollectorConfig `yaml:"collectors"`
	ChronyConfig ChronyConfig    `yaml:"chrony_config"`
	NTPConfig    NTPConfig       `yaml:"ntp_config"`
}

type CollectorConfig struct {
	Chrony *bool `yaml:"chrony"`
	Time   *bool `yaml:"time"`
	Timex  *bool `yaml:"timex"`
	NTP    *bool `yaml:"ntp"`
}

type ChronyConfig struct {
	ChronyAddress string `yaml:"chrony_address"`
	ChronyPort    int    `yaml:"chrony_port"`
}

func LoadConfig(filePath string) *TimeConfig {
	if strings.TrimSpace(filePath) == "" {
		logrus.Warn("empty yaml config path, using default config")
		return DefaultTimeConfig()
	}

	absFilePath, err := filepath.Abs(filePath)
	if err != nil {
		logrus.Warnf("can't normalize yaml config path: %v", err)
		return DefaultTimeConfig()
	}

	yamlContent, err := os.ReadFile(absFilePath)
	if err != nil {
		logrus.Warnf("can't read yaml config: %v", err)
		return DefaultTimeConfig()
	}

	var configFile TimeConfig
	if err := yaml.Unmarshal(yamlContent, &configFile); err != nil {
		logrus.Warnf("can't parse yaml config: %v", err)
		return DefaultTimeConfig()
	}

	configFile.applyDefaults()
	return &configFile
}

// DefaultTimeConfig 默认配置
func DefaultTimeConfig() *TimeConfig {
	return &TimeConfig{
		Collectors: defaultCollectorConfig(),
		NTPConfig: NTPConfig{
			NtpServer:          "pool.ntp.org",
			NtpProtocolVersion: 4,
			NtpServerIsLocal:   false,
			NtpIPTTL:           1,
		},
		ChronyConfig: ChronyConfig{
			ChronyAddress: "pool.ntp.org",
			ChronyPort:    323,
		},
	}
}

func defaultCollectorConfig() CollectorConfig {
	t := true
	return CollectorConfig{
		Chrony: &t,
		Time:   &t,
		Timex:  &t,
		NTP:    &t,
	}
}

func (cfg *TimeConfig) applyDefaults() {
	if cfg.Collectors.Chrony == nil || cfg.Collectors.Time == nil || cfg.Collectors.Timex == nil || cfg.Collectors.NTP == nil {
		defaults := defaultCollectorConfig()
		if cfg.Collectors.Chrony == nil {
			cfg.Collectors.Chrony = defaults.Chrony
		}
		if cfg.Collectors.Time == nil {
			cfg.Collectors.Time = defaults.Time
		}
		if cfg.Collectors.Timex == nil {
			cfg.Collectors.Timex = defaults.Timex
		}
		if cfg.Collectors.NTP == nil {
			cfg.Collectors.NTP = defaults.NTP
		}
	}
}

func (cc CollectorConfig) enabled(name string) bool {
	switch name {
	case "chrony":
		return cc.Chrony != nil && *cc.Chrony
	case "time":
		return cc.Time != nil && *cc.Time
	case "timex":
		return cc.Timex != nil && *cc.Timex
	case "ntp":
		return cc.NTP != nil && *cc.NTP
	default:
		return true
	}
}

func SetConfigPath(path string) {
	configMu.Lock()
	defer configMu.Unlock()

	trimmed := strings.TrimSpace(path)
	if trimmed == "" {
		logrus.Debug("received empty metrics config path, fallback to default")
		activeConfigPath = defaultConfigPath
	} else {
		activeConfigPath = trimmed
	}
	activeConfig = nil
}

func getConfig() *TimeConfig {
	configMu.RLock()
	cached := activeConfig
	path := activeConfigPath
	configMu.RUnlock()

	if cached != nil {
		return cached
	}

	loaded := LoadConfig(path)

	configMu.Lock()
	if activeConfig == nil {
		activeConfig = loaded
	}
	result := activeConfig
	configMu.Unlock()

	return result
}
